///////////////////////////////////////////////////////////////////////////////
//
// Licensed under the MIT License
//
// Copyright (c) 2010 Chris Colinsky
// All Rights Reserved
//
// version	0.5
// http://code.google.com/p/sugarcookie/
// http://code.google.com/p/sugarcookie/wiki/License
//
////////////////////////////////////////////////////////////////////////////////

package sugarcookie.framework {
	import com.adobe.serialization.json.JSON;
	
	import flash.events.EventDispatcher;
	import flash.utils.getDefinitionByName;
	
	import mx.logging.Log;
	
	import sugarcookie.data.DataChannels;
	import sugarcookie.data.handlers.IDataHandler;
	import sugarcookie.data.handlers.config.ConfigurationHandler;
	import sugarcookie.data.handlers.config.vo.ViewConfig;
	import sugarcookie.error.ApplicationError;
	import sugarcookie.events.DataEvent;
	import sugarcookie.framework.IModel;

	/**
	 * Manages multiple data sources.
	 */
	public class Model extends DataChannels implements IModel {
		public static var VIEW_MAP:Array;
		
		private static var _instance:Model = null;
		
		protected var _lang:String;
		protected var _initDeepLink:String;
		protected var _hostPage:String;
		protected var _autoLoadHandlerArgs:Array;
		protected var _autoLoadChannelId:String = "main";
		protected var _autoLoadDataHandlerClassDef:String;
		protected var setCustomAutoLoad:Boolean = false;
	
		public function Model(config:String,initDeepLink:String="/",hostPage:String="home",lang:String="EN") {
			super();
			_log = Log.getLogger("sugarcookie.data.DataManager");
			_lang = lang;
			_initDeepLink = initDeepLink;
			_hostPage = hostPage;
			
			if (config!="") initConfig(config);
			else _log.warn("No configuration xml was defined for the Application.");
		}
		
		public function get lang():String {
			return _lang;
		}
		
		public function getPath(id:String):String {
			if (isChannelAvailable("config")) throw new ApplicationError("No configuration xml was defined for the Application.");
			return ConfigurationHandler(getDataChannel("config")).getPath(id);
		}
		
		public function getURL(id:String):String {
			if (isChannelAvailable("config")) throw new ApplicationError("No configuration xml was defined for the Application.");
			return ConfigurationHandler(getDataChannel("config")).getURL(id);
		}
		
		public function getJSFunction(id:String):String {
			if (isChannelAvailable("config")) throw new ApplicationError("No configuration xml was defined for the Application.");
			return ConfigurationHandler(getDataChannel("config")).getJSFunction(id);
		}
		
		public function getService(id:String):String {
			if (isChannelAvailable("config")) throw new ApplicationError("No configuration xml was defined for the Application.");
			return ConfigurationHandler(getDataChannel("config")).getService(id);
		}
		
		public function getIntro():String {
			if (isChannelAvailable("config")) throw new ApplicationError("No configuration xml was defined for the Application.");
			return ConfigurationHandler(getDataChannel("config")).getIntroURI();
		}
		
		public function get defaultPageTitle():String {
			if (isChannelAvailable("config")) throw new ApplicationError("No configuration xml was defined for the Application.");
			return ConfigurationHandler(getDataChannel("config")).defualtPageTitle;
		}
		
		public function get hasIntro():Boolean {
			if (isChannelAvailable("config")) throw new ApplicationError("No configuration xml was defined for the Application.");
			return ConfigurationHandler(getDataChannel("config")).hasIntro;
		}
		
		public function get autoLoadChannelId():String {
			return _autoLoadChannelId;
		}
		
		public function get initDeepLink():String {
			return _initDeepLink;
		}
		
		protected function init():void {
			//to be overridden in subclass and allows for custom setup to happen automatically
			_log.debug("DataManager.init() can be overridden in the sub-class to facilitate additional custom startup procedures after the config.xml has been loaded.");
		}
		
		private function initConfig(url:String):void {
			var dc:IDataHandler = new ConfigurationHandler(url);
			dc.addEventListener(DataEvent.ON_CONFIG_INIT,onConfigInit,false,0,true);
			addDataChannel("config",dc);
		}
		
		private function onConfigInit(evt:DataEvent):void {
			getDataChannel("config").removeEventListener(DataEvent.ON_CONFIG_INIT, onConfigInit);
			
			Model.VIEW_MAP = getDataChannel("config").getViewMap();
			
			if (evt.status=="CONFIG") {
				init();
				dispatchEvent(new DataEvent(DataEvent.ON_DATA_INIT,false,false,null,true));
			} else {
				_autoLoadHandlerArgs = evt.data.handler_args.split(",");
				_autoLoadChannelId = evt.data.id;
				_autoLoadDataHandlerClassDef = ConfigurationHandler(getDataChannel("config")).getDefaultAutoLoadClassDef();
				if (_autoLoadHandlerArgs==null || _autoLoadChannelId==null) throw new ApplicationError("Necessary values have not been set for either _autoLoadHandlerArgs or _autoLoadChannelId.  check the config.xml for proper settings.");
				
				if (evt.data.dynamicAutoLoad) setAutoLoadParams(_autoLoadHandlerArgs,_autoLoadChannelId);

				doAutoLoad();
			}
		}
		
		protected function setAutoLoadParams(default_handler_args:Array,default_id:String):void {
			//can be overridden in subclass
			_log.debug("DataEvent.setAutoLoadParams() - override this method to apply custom logic to determining the file to autoload based on SWFAddress.getPath() values.");
			_log.debug("_initDeepLink: "+_initDeepLink+", _hostPage: "+_hostPage);
			if (_initDeepLink=="/" && _hostPage!="home") {
				if (validateId(_hostPage)) {
					updateInitDeepLink();
					_autoLoadHandlerArgs = Model.VIEW_MAP[_hostPage].dataHandlerArgs;
					_autoLoadDataHandlerClassDef = Model.VIEW_MAP[_hostPage].dataHandlerClassDef;
					_autoLoadChannelId = _hostPage;
				}
			} else {
				var arr:Array = _initDeepLink.substr(1).split("/");
				if(validateId(arr[0])) {
					_autoLoadHandlerArgs = JSON.decode(Model.VIEW_MAP[arr[0]].dataHandlerArgs);
					_autoLoadDataHandlerClassDef = Model.VIEW_MAP[arr[0]].dataHandlerClassDef;
					_autoLoadChannelId = arr[0];
				}
			}
		}
		
		private function updateInitDeepLink():void {
			var _viewMap:Array = Model.VIEW_MAP;
			if (ViewConfig(_viewMap[_hostPage]).uri!=null) {
				_initDeepLink = ViewConfig(_viewMap[_hostPage]).uri;
				return;
			} else if (ViewConfig(_viewMap[_hostPage]).children.length>0) {
				for (var i:int=0;i<ViewConfig(_viewMap[_hostPage]).children.length;i++) {
					if (ViewConfig(_viewMap[_hostPage].children[i]).uri!=null) {
						_initDeepLink = ViewConfig(_viewMap[_hostPage].children[i]).uri;
						return
					}
				}
				_log.error("No default uri information can be found for host page!");
			}
		}
		
		private function validateId(id:String):Boolean {
			//check for valid top level view
			var _viewMap:Array = Model.VIEW_MAP;
			for (var i:String in _viewMap) if (i==id) return true;
			return false;
		}
		
		final protected function doAutoLoad():void { 
			_log.debug("_autoLoadHandlerArgs: "+_autoLoadHandlerArgs+", _autoLoadChannelId: "+_autoLoadChannelId);
			var AutoLoadClass:Class = Class(getDefinitionByName( _autoLoadDataHandlerClassDef ));
			var autoLoadHandler:IDataHandler = new AutoLoadClass(_autoLoadHandlerArgs);
			autoLoadHandler.autoLoad = true;
			autoLoadHandler.addEventListener(DataEvent.ON_AUTO_LOAD_COMPLETE,onAutoLoadComplete);
			addDataChannel(_autoLoadChannelId,autoLoadHandler);
		}
		
		private function onAutoLoadComplete(evt:DataEvent):void {
			init();
			getDataChannel(_autoLoadChannelId).removeEventListener(DataEvent.ON_AUTO_LOAD_COMPLETE, onAutoLoadComplete);
			dispatchEvent(new DataEvent(DataEvent.ON_DATA_INIT,false,false,null,true));
		}
	}
}